Emberveil sourcegame/2006Scape Server/src/main/java/org/apollo/cache/map/MapFileDecoder.java
package org.apollo.cache.map;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.util.CompressionUtil;
import java.nio.ByteBuffer;
import java.io.IOException;
/**
* A decoder for the terrain data stored in {@link MapFile}s.
*
* @author Major
*/
public class MapFileDecoder {
/**
* Creates a MapFileDecoder for the specified map file.
*
* @param fs The {@link IndexedFileSystem} to get the file from.
* @param index The {@link MapIndex} to get the file index from.
* @return The MapFileDecoder.
* @throws IOException If there is an error reading or decompressing the file.
*/
public static MapFileDecoder create(IndexedFileSystem fs, MapIndex index) throws IOException {
ByteBuffer compressed = fs.getFile(MapConstants.MAP_INDEX, index.getMapFile());
ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(compressed));
return new MapFileDecoder(decompressed);
}
/**
* The DataBuffer containing the MapFile data.
*/
private final ByteBuffer buffer;
/**
* Creates the MapIndexDecoder.
* <p>
* This constructor expects the {@link ByteBuffer} to <strong>not</strong> be compressed.
*
* @param buffer The DataBuffer containing the MapFile data.
*/
public MapFileDecoder(ByteBuffer buffer) {
this.buffer = buffer.asReadOnlyBuffer();
}
/**
* Decodes the data into a {@link MapFile}.
*
* @return The MapFile.
*/
public MapFile decode() {
MapPlane[] planes = new MapPlane[MapConstants.MAP_PLANES];
for (int level = 0; level < MapConstants.MAP_PLANES; level++) {
planes[level] = decodePlane(planes, level);
}
return new MapFile(planes);
}
/**
* Decodes a {@link MapPlane} with the specified level.
*
* @param planes The previously-decoded {@link MapPlane}s, for calculating the height of the tiles.
* @param level The level.
* @return The MapPlane.
*/
private MapPlane decodePlane(MapPlane[] planes, int level) {
Tile[][] tiles = new Tile[MapConstants.MAP_WIDTH][MapConstants.MAP_WIDTH];
for (int x = 0; x < MapConstants.MAP_WIDTH; x++) {
for (int z = 0; z < MapConstants.MAP_WIDTH; z++) {
tiles[x][z] = decodeTile(planes, level, x, z);
}
}
return new MapPlane(level, tiles);
}
/**
* Decodes the data into a {@link Tile}.
*
* @param planes The previously-decoded {@link MapPlane}s, for calculating the height of the Tile.
* @param level The level the Tile is on.
* @param x The x coordinate of the Tile.
* @param z The z coordinate of the Tile.
* @return The MapFile.
*/
private Tile decodeTile(MapPlane[] planes, int level, int x, int z) {
Tile.Builder builder = Tile.builder(x, z, level);
int type;
do {
type = buffer.get() & 0xFF;
if (type == 0) {
if (level == 0) {
builder.setHeight(TileUtils.calculateHeight(x, z));
} else {
Tile below = planes[level - 1].getTile(x, z);
builder.setHeight(below.getHeight() + MapConstants.PLANE_HEIGHT_DIFFERENCE);
}
} else if (type == 1) {
int height = buffer.get();
int below = (level == 0) ? 0 : planes[level - 1].getTile(x, z).getHeight();
builder.setHeight((height == 1 ? 0 : height) * MapConstants.HEIGHT_MULTIPLICAND + below);
} else if (type <= MapConstants.MINIMUM_OVERLAY_TYPE) {
builder.setOverlay(buffer.get());
builder.setOverlayType((type - MapConstants.LOWEST_CONTINUED_TYPE)
/ MapConstants.ORIENTATION_COUNT);
builder.setOverlayOrientation(type - MapConstants.LOWEST_CONTINUED_TYPE
% MapConstants.ORIENTATION_COUNT);
} else if (type <= MapConstants.MINIMUM_ATTRIBUTES_TYPE) {
builder.setAttributes(type - MapConstants.MINIMUM_OVERLAY_TYPE);
} else {
builder.setUnderlay(type - MapConstants.MINIMUM_ATTRIBUTES_TYPE);
}
} while (type >= MapConstants.LOWEST_CONTINUED_TYPE);
return builder.build();
}
}